﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using DotNetDAL.Entity;

namespace DotNetDAL.Extensions
{

    /// <summary>
    /// 实体字段属性值复制标志。实现该接口的类，有 Copy<T> 方法
    /// </summary>
    public interface ICopyEntity { }

    /// <summary>
    /// 扩展方法
    /// </summary>
    public static class ExtensionMethods
    {

        #region DataTable

        /// <summary>
        /// 获取第一行数据
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static Dictionary<string, object> GetFirstOrDefault (this DataTable dt)
        {
            if (dt.Rows.Count == 0) {
                return default(Dictionary<string, object>);
            }
            DataRow dr = dt.Rows[0];
            Dictionary<string, object> row = new Dictionary<string, object>();
            foreach (DataColumn dc in dt.Columns)
            {
                row[dc.ColumnName] = dr[dc.ColumnName];
            }

            return row;
        }

        /// <summary>
        /// 转换为字典集合
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<Dictionary<string, object>> ToDictionaryList(this DataTable dt) {
            List<Dictionary<string, object>> table = new List<Dictionary<string, object>>();
            foreach (DataRow dr in dt.Rows)
            {
                Dictionary<string, object> row = new Dictionary<string, object>();
                foreach (DataColumn dc in dt.Columns)
                {
                    row[dc.ColumnName] = dr[dc.ColumnName];
                }
                table.Add(row);
            }
            return table;
        }

        /// <summary>
        /// DataTable 转换为字典
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="majorKeyColumn"></param>
        /// <returns></returns>
        public static Dictionary<string, T> ToDictionary<T>(this DataTable dt,string majorKeyColumn) where T:class,new()
        {
            Type T_type = typeof(T);
            
            Dictionary<string, T> table = new Dictionary<string, T>();
            foreach (DataRow dr in dt.Rows)
            {
                string majorKeyValue = Convert.ToString(dr[majorKeyColumn]);
                T row = new T();
                foreach (DataColumn dc in dt.Columns)
                {
                    //字段
                    FieldInfo Field = T_type.GetField(dc.ColumnName);
                    if (Field != null)
                    {
                        Field.SetValue(row, dr[dc.ColumnName]);
                    }
                    else {

                        //属性
                        PropertyInfo Property = T_type.GetProperty(dc.ColumnName);
                        if (Property != null) {
                            Property.SetValue(row, dr[dc.ColumnName]);
                        }
                        
                    }
                }

                /*基础数据类型
                        bool	
                        byte	
                        char	
                        decimal	
                        double	
                        float	
                        int	
                        long	
                        sbyte	
                        short	
                        uint	
                        ulong	
                        ushort	

                        object
                        string
                        DateTime
                 */

                table.Add(majorKeyValue,row);
            }
            return table;
        }

        /// <summary>
        /// DataTable 转换为字典
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="majorKeyColumn"></param>
        /// <returns></returns>
        public static Dictionary<string, Dictionary<string, object>> ToDictionary(this DataTable dt, string majorKeyColumn)
        {
            Dictionary<string, Dictionary<string, object>> table = new Dictionary<string, Dictionary<string, object>>();
            foreach (DataRow dr in dt.Rows)
            {
                string majorKeyValue = Convert.ToString(dr[majorKeyColumn]);
                Dictionary<string, object> row = new Dictionary<string, object>();
                foreach (DataColumn dc in dt.Columns)
                {
                    row[dc.ColumnName] = dr[dc.ColumnName];
                }
                table.Add(majorKeyValue, row);
            }
            return table;
        }

        /// <summary>
        /// DataTable 转成 List
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> ToList<T>(this DataTable dt) where T:class
        {
            List<T> list = new List<T>();
            foreach (DataRow item in dt.Rows)
            {
                T row = ToObject<T>(item);
                if (row != null) {
                    list.Add(row);
                }
            }
            return list;
        }

        /// <summary>
        /// DataTable 转成 String List
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<string> ToStringList(this DataTable dt,string ColumnName) {
            List<string> list = new List<string>();
            foreach (DataRow item in dt.Rows) {
                list.Add(Convert.ToString(item[ColumnName]));
            }
            return list;
        }


        /// <summary>
        /// 生成分页数据
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="PageSize">每页大小</param>
        /// <param name="pageNumber">页码</param>
        /// <returns></returns>
        public static PagingData ToPagingData(this DataTable dt, int PageSize, int pageNumber) {
            PagingData pd = new PagingData();
            pd.pagenumber = pageNumber;
            pd.pagesize = PageSize;
            pd.total = dt.Rows.Count;

            if (pageNumber < 1)
            {
                pageNumber = 1;
            }
            /*
            int minIndex = PageSize * (pageNumber - 1) + 1;
            int maxIndex = PageSize * pageNumber; 
            */

            int minIndex = PageSize * (pageNumber - 1);//从零开始
            int maxIndex = PageSize * pageNumber - 1;//1210
            int rowMaxIndex = (pd.total - 1) >= maxIndex ? maxIndex : (pd.total - 1);

            using (DataTable pageTable = dt.Clone())
            {
                pageTable.Columns.Add("__RowNumber__", typeof(int));
                for (int i = minIndex; i <= rowMaxIndex; i++)
                {
                    DataRow pageRow = pageTable.NewRow();
                    DataRow dRow = dt.Rows[i];
                    foreach (DataColumn column in dt.Columns)
                    {
                        pageRow[column.ColumnName] = dRow[column.ColumnName];
                    }
                    pageRow["__RowNumber__"] = i + 1;

                    pageTable.Rows.Add(pageRow);
                }
                pd.rows = pageTable;
            }

            return pd;
        }


        #endregion

        #region DataSet

        /// <summary>
        /// DataSet 转成 List
        /// </summary>
        /// <param name="ds"></param>
        /// <returns></returns>
        public static List<List<Dictionary<string, object>>> ToDictionaryList(this DataSet ds) {
            List<List<Dictionary<string, object>>> resList = new List<List<Dictionary<string, object>>>();
            for (int ind = 0; ind < ds.Tables.Count; ind++) {
                resList.Add(ds.Tables[ind].ToDictionaryList());
            }
            return resList;
        }


        #endregion

        #region List<T>


        #region ToDataTable 

        //代码收集

        /// <summary>
        /// 将实体集合转换为DataTable
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="entities">实体集合</param>
        public static DataTable ToDataTable<T>(this List<T> entities)
        {
            try
            {
                var result = CreateTable<T>();
                FillData(result, entities);
                return result;
            }
            catch (Exception exp) {
                throw new Exception("将实体集合转换为 DataTable 异常。", exp);
            }
        }

        /// <summary>
        /// 创建表
        /// </summary>
        private static DataTable CreateTable<T>()
        {
            var result = new DataTable();
            var type = typeof(T);
            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var propertyType = property.PropertyType;
                if ((propertyType.IsGenericType) && (propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                    propertyType = propertyType.GetGenericArguments()[0];
                result.Columns.Add(property.Name, propertyType);
            }
            return result;
        }

        /// <summary>
        /// 填充数据
        /// </summary>
        private static void FillData<T>(DataTable dt, IEnumerable<T> entities)
        {
            foreach (var entity in entities)
            {
                dt.Rows.Add(CreateRow(dt, entity));
            }
        }

        /// <summary>
        /// 创建行
        /// </summary>
        private static DataRow CreateRow<T>(DataTable dt, T entity)
        {
            DataRow row = dt.NewRow();
            var type = typeof(T);
            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                row[property.Name] = property.GetValue(entity) ?? DBNull.Value;
            }
            return row;
        }

        #endregion

        #region SortBy

        public class ListSortByInfo
        {
            public string sortName = null;
            public string sortOrder = null;
        }
        /// <summary>
        /// 排序。排序字符串。像SQL 的order by 后面的内容。如：“AA”,“AA asc”，“AA desc,BB”
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="OrderBy">排序字符串。像SQL 的order by 后面的内容。如：“AA”,“AA asc”，“AA desc,BB”</param>
        /// <returns></returns>
        public static List<T> SortBy<T>(this List<T> list, string OrderBy) where T : class
        {
            if (list.Count < 2)
            {
                return list;
            }

            List<ListSortByInfo> OrderByInfoList = new List<ListSortByInfo>();
            T row = list[0];
            Type type = row.GetType();
            if (!string.IsNullOrEmpty(OrderBy))
            {
                string[] OrderBys = OrderBy.Trim().Split(new char[] { ',' });
                for (int ind = 0; ind < OrderBys.Length; ind++)
                {
                    string _OrderBy = OrderBys[ind].Trim();
                    string sortName = _OrderBy.Split(new char[] { ' ' })[0];
                    string sortOrder = _OrderBy.Replace(sortName + " ", "").Trim();
                    if (!string.IsNullOrEmpty(sortName) && (type.GetProperty(sortName) != null || type.GetField(sortName) != null))
                    {
                        OrderByInfoList.Add(new ListSortByInfo
                        {
                            sortName = sortName,
                            sortOrder = sortOrder
                        });
                    }
                }
            }
            if (OrderByInfoList.Count > 0)
            {
                ListSortByInfo sort01 = OrderByInfoList[0];
                var sortData = sort01.sortOrder == "desc" ? list.OrderByDescending(row2 => GetObjectValue(row2, sort01.sortName))
                    : list.OrderBy(row2 => GetObjectValue(row2, sort01.sortName));

                if (OrderByInfoList.Count > 1)
                {
                    for (int ind = 1; ind < OrderByInfoList.Count; ind++)
                    {
                        ListSortByInfo sort02 = OrderByInfoList[ind];
                        sortData = sort01.sortOrder == "desc" ? sortData.ThenByDescending(row2 => GetObjectValue(row2, sort01.sortName))
                            : sortData.ThenBy(row2 => GetObjectValue(row2, sort01.sortName));
                    }
                }

                return sortData.ToList();

            }
            return list;
        }
        /// <summary>
        /// 获取类公共字段/属性的值
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private static object GetObjectValue<T>(T obj, string key) where T : class
        {
            Type type = obj.GetType();
            FieldInfo fieldInfo = type.GetField(key);
            if (fieldInfo != null)
            {
                return fieldInfo.GetValue(obj);
            }
            PropertyInfo propertyInfo = type.GetProperty(key);
            if (propertyInfo != null)
            {
                return propertyInfo.GetValue(obj);
            }
            return null;
        }
        #endregion


        #endregion

        #region DataRow

        /// <summary>
        /// DataRow 转成 T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="row"></param>
        /// <returns></returns>
        public static T ToObject<T>(this DataRow row) where T : class
        {

            DataColumnCollection columns = row.Table.Columns;
            Type type = typeof(T);
            List<PropertyInfo> plist = new List<PropertyInfo>(type.GetProperties());
            List<FieldInfo> flist = new List<FieldInfo>(type.GetFields());
            T rowData = Activator.CreateInstance<T>();

            bool isFillData = false;
            foreach (DataColumn dc in columns)
            {

                PropertyInfo pInfo = plist.Find(p => p.Name == dc.ColumnName);
                if (pInfo != null)
                {
                    try
                    {
                        var pValue = row[dc.ColumnName];
                        if (!Convert.IsDBNull(pValue))
                        {
                            string _PropertyType = pInfo.PropertyType.ToString();
                            object pVal = null;
                            if (pValue == null)
                            {
                                pVal = pValue;
                            }
                            else if (_PropertyType== "System.String") {
                                pVal = Convert.ToString(pValue);
                            }
                            else if (_PropertyType.Contains("System.Nullable"))
                            {
                                pVal = Convert.ChangeType(pValue, Nullable.GetUnderlyingType(pInfo.PropertyType));
                            }
                            else
                            {
                                pVal = Convert.ChangeType(pValue, pInfo.PropertyType);
                            }
                            if (pVal != null) {
                                pInfo.SetValue(rowData, pVal);

                                if (!isFillData) {
                                    isFillData = true;
                                }

                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("属性[" + pInfo.Name + "]值转换出错," + ex.Message, ex);
                    }
                }


                FieldInfo fInfo = flist.Find(f => f.Name == dc.ColumnName);
                if (fInfo != null)
                {
                    try
                    {
                        var fValue = row[dc.ColumnName];
                        if (!Convert.IsDBNull(fValue))
                        {
                            object fVal = null;
                            string _FieldType = fInfo.FieldType.ToString();
                            if (fValue == null)
                            {
                                fVal = fValue;
                            }
                            else if(_FieldType == "System.String")
                            {
                                fVal = Convert.ToString(fValue);
                            }
                            else if (_FieldType.Contains("System.Nullable"))
                            {
                                fVal = Convert.ChangeType(fValue, Nullable.GetUnderlyingType(fInfo.FieldType));
                            }
                            else
                            {
                                fVal = Convert.ChangeType(fValue, fInfo.FieldType);
                            }

                            if (fVal != null) {
                                fInfo.SetValue(rowData, fVal);

                                if (!isFillData)
                                {
                                    isFillData = true;
                                }
                            }
                            
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception("字段[" + fInfo.Name + "]转换出错," + ex.Message, ex);
                    }
                }

            }

            return isFillData ? rowData : default(T);
        }

        #endregion

        #region DataRow[]

        /// <summary>
        /// DataRow[] 转成 List<T>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> ToList<T>(this DataRow[] rows) where T:class
        {
            List<T> list = new List<T>();
            if (rows.Length == 0)
            {
                return list;
            }
            foreach (DataRow item in rows)
            {
                T row = item.ToObject<T>();
                if (row != null) {
                    list.Add(row);
                }
                
            }
            return list;
        }

        #endregion

        #region 字段属性值复制

        /// <summary>
        /// 字段属性值复制
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fData"></param>
        /// <returns></returns>
        public static T Copy<T>(this ICopyEntity fData) where T : class
        {
            return Utils.UtilPackage.Copy<T>(fData);
        }

        #endregion

    }

}